//Download by http://www.NewXing.com
// FCompareDlg.cpp : implementation file
// (c) Codeguru & friends
// Coded by Antonio Tejada Lacaci. 1999
// atejada@espanet.com
//

#include "stdafx.h"
#include "FCompare.h"
#include "FCompareDlg.h"
#include "DirDialog.h"
#include <Shlwapi.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

#include "HyperLink.h"

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	CHyperLink m_urlHomepage;
	CHyperLink m_urlCodeguru;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	DDX_Control(pDX, URL_HOMEPAGE, m_urlHomepage);
	DDX_Control(pDX, URL_CODEGURU, m_urlCodeguru);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFCompareDlg dialog

CFCompareDlg::CFCompareDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CFCompareDlg::IDD, pParent) {
	//{{AFX_DATA_INIT(CFCompareDlg)
	m_bRecurse = FALSE;
	m_nMatchCriteria = 0;
	m_dwUpto = 0;
	m_strAllowedMasks = _T("*.*");
	m_strDirectory = _T("");
	m_bCompareDuplicates = FALSE;
	//}}AFX_DATA_INIT
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

   // Flags
   m_bExit = FALSE;
   m_bAbort = FALSE;
   m_bAddInProgress = FALSE;
   m_bFillInProgress = FALSE;
   m_bCompareInProgress = FALSE;
   m_bsState = BS_IDLE;

   // Logical data
   m_msaMatchedInfos.SetSize(0,20);
}

void CFCompareDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CFCompareDlg)
	DDX_Control(pDX, LBL_STATUS, m_lblStatus);
	DDX_Control(pDX, LBL_TARGETFILES, m_lblTargetFiles);
	DDX_Control(pDX, LBL_SOURCEFILES, m_lblSourceFiles);
	DDX_Control(pDX, LBL_MATCHEDFILES, m_lblMatchedFiles);
	DDX_Control(pDX, LST_MATCHEDFILES, m_lstMatchedFiles);
	DDX_Control(pDX, TAB_ACTION, m_tabAction);
	DDX_Control(pDX, LST_TARGETFILES, m_lstTargetFiles);
	DDX_Control(pDX, LST_SOURCEFILES, m_lstSourceFiles);
	DDX_Control(pDX, PRG_PROGRESS, m_prgProgress);
	DDX_Check(pDX, CHK_RECURSEDIR, m_bRecurse);
	DDX_Radio(pDX, RAD_MATCHCRITERIA, m_nMatchCriteria);
	DDX_Text(pDX, EDT_UPTO, m_dwUpto);
	DDX_Text(pDX, EDT_ALLOWEDMASKS, m_strAllowedMasks);
	DDX_Text(pDX, EDT_DIRECTORY, m_strDirectory);
	DDX_Check(pDX, CHK_COMPAREDUPLICATES, m_bCompareDuplicates);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CFCompareDlg, CDialog)
	//{{AFX_MSG_MAP(CFCompareDlg)
   ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(RAD_MATCHCONTENTS, OnMatchContents)
	ON_BN_CLICKED(RAD_MATCHCRITERIA, OnMatchCriteria)
	ON_BN_CLICKED(BTN_COMPARE, OnCompare)
	ON_WM_TIMER()
	ON_NOTIFY(LVN_GETDISPINFO, LST_SOURCEFILES, OnGetdispinfoSearchFiles)
	ON_BN_CLICKED(BTN_EXIT, OnExit)
	ON_NOTIFY(TCN_SELCHANGE, TAB_ACTION, OnSelchangeAction)
	ON_NOTIFY(LVN_GETDISPINFO, LST_MATCHEDFILES, OnGetdispinfoMatchedFiles)
	ON_BN_CLICKED(BTN_EXPLOREDIR, OnExploreDirectory)
	ON_BN_CLICKED(BTN_ADDSOURCEFILES, OnAddSourceFiles)
	ON_BN_CLICKED(BTN_ADDTARGETFILES, OnAddTargetFiles)
	ON_BN_CLICKED(BTN_CLEARSOURCEFILES, OnClearSourceFiles)
	ON_BN_CLICKED(BTN_CLEARTARGETFILES, OnClearTargetFiles)
	ON_WM_CLOSE()
	ON_NOTIFY(LVN_GETDISPINFO, LST_TARGETFILES, OnGetdispinfoSearchFiles)
	ON_BN_CLICKED(RAD_MATCHCHECKSUM, OnMatchContents)
	ON_BN_CLICKED(RAD_MATCHCRC, OnMatchContents)
	ON_BN_CLICKED(BTN_EXPORT, OnExport)
	//}}AFX_MSG_MAP
   ON_MESSAGE(WM_SEARCH_FINISHED, OnSearchFinished)
   ON_MESSAGE(WM_COMPARE_FINISHED, OnCompareFinished)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFCompareDlg message handlers

BOOL CFCompareDlg::OnInitDialog() {
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
   if (pSysMenu != NULL) {
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty()) {
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
      // Invalidate maximize & resize
      pSysMenu->EnableMenuItem(SC_SIZE,MF_GRAYED);
      pSysMenu->EnableMenuItem(SC_MAXIMIZE,MF_GRAYED);
      pSysMenu->EnableMenuItem(SC_RESTORE,MF_GRAYED);
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
   m_lstSourceFiles.InsertColumn(0,_T("Filename"),LVCFMT_LEFT,300);
   m_lstSourceFiles.InsertColumn(1,_T("Size"),LVCFMT_RIGHT,65);

   m_lstTargetFiles.InsertColumn(0,_T("Filename"),LVCFMT_LEFT,300);
   m_lstTargetFiles.InsertColumn(1,_T("Size"),LVCFMT_RIGHT,65);

   m_lstMatchedFiles.InsertColumn(0,_T("Source Filename"),LVCFMT_LEFT,268);
   m_lstMatchedFiles.InsertColumn(1,_T("Target Filename"),LVCFMT_LEFT,268);
   m_lstMatchedFiles.InsertColumn(2,_T("Size"),LVCFMT_RIGHT,65);

   UpdateCounters();

   m_tabAction.InsertItem(PPG_SEARCH,_T(" Search "));
   m_tabAction.InsertItem(PPG_COMPARE,_T(" Compare "));

   GetDlgItem(LBL_TARGETFILES)->ShowWindow(SW_SHOW);
   GetDlgItem(LBL_SOURCEFILES)->ShowWindow(SW_SHOW);
   GetDlgItem(LST_TARGETFILES)->ShowWindow(SW_SHOW);
   GetDlgItem(LST_SOURCEFILES)->ShowWindow(SW_SHOW);
   GetDlgItem(CHK_RECURSEDIR)->ShowWindow(SW_SHOW);
   GetDlgItem(BTN_COMPARE)->ShowWindow(SW_SHOW);
   GetDlgItem(LBL_MATCHEDFILES)->ShowWindow(SW_HIDE);
   GetDlgItem(LST_MATCHEDFILES)->ShowWindow(SW_HIDE);

   UpdateData(FALSE);
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CFCompareDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CFCompareDlg::OnPaint() {
	if (IsIconic()) {
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
   } else {
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CFCompareDlg::OnQueryDragIcon() {
	return (HCURSOR) m_hIcon;
}

void CFCompareDlg::OnExploreDirectory() {
   CDirDialog dlgDir;
   
   dlgDir.m_strTitle = _T("Select directory for file search");
   UpdateData(TRUE);
   dlgDir.m_strSelDir = m_strDirectory;
   dlgDir.m_strWindowTitle = _T("Select directory");
   if (dlgDir.DoBrowse(this) == IDOK) {
      m_strDirectory = dlgDir.m_strPath;
      // Append backslash if necessary
      if ((m_strDirectory.GetLength()>0) && 
          (m_strDirectory[m_strDirectory.GetLength()-1] != TCHAR('\\'))) 
          m_strDirectory += TCHAR('\\');
      UpdateData(FALSE);
   }
}

void CFCompareDlg::OnMatchContents() {
   GetDlgItem(EDT_UPTO)->EnableWindow(TRUE);	
}

void CFCompareDlg::OnMatchCriteria() {
   GetDlgItem(EDT_UPTO)->EnableWindow(FALSE);
}

UINT SearchThread(CFCompareDlg *pcdlg) {
   // Retrieve directories
   LVITEM lvi;
   CString strMask;
   int nPos=0;
   int nNextPos=0;

   memset(&lvi,0,sizeof(lvi));
   pcdlg->m_bAddInProgress = TRUE;

   // Delete files' listview (if files were added, listviews are no longer up to date due 
   // to possible reordering of items in FileInfoArray).
   pcdlg->m_plstFiles->DeleteAllItems();

   // Retrieve files: Sorted by size and ascending, do not include dirs.
   while (nPos < pcdlg->m_strAllowedMasks.GetLength()) {
      nNextPos = pcdlg->m_strAllowedMasks.Find(";",nPos);
      if (nNextPos == -1) nNextPos = pcdlg->m_strAllowedMasks.GetLength();
      strMask = pcdlg->m_strAllowedMasks.Mid(nPos, nNextPos-nPos);
      strMask.TrimLeft();
      strMask.TrimRight();
      TRACE("strMask is %s\n",strMask);
      if (strMask != "")
         pcdlg->m_pfiaInfos->AddDir(pcdlg->m_strDirectory, strMask, pcdlg->m_bRecurse, 
            CFileInfoArray::AP_SORTBYSIZE | CFileInfoArray::AP_SORTASCENDING, FALSE, 
            &pcdlg->m_bAbort);
      nPos = nNextPos+1;
   }

   pcdlg->m_lblStatus.SetWindowText(_T("Filling listview..."));

   pcdlg->m_ulProgress = 0;
   pcdlg->m_ulProgressMax = pcdlg->m_pfiaInfos->GetSize();

   pcdlg->m_bAddInProgress = FALSE;
   pcdlg->m_bFillInProgress = TRUE;
   

   // Set common LVITEM attributes
   lvi.pszText = LPSTR_TEXTCALLBACK;
   lvi.mask = LVIF_TEXT | LVIF_PARAM;
   lvi.iSubItem = 0;

   // Insert files
   pcdlg->m_plstFiles->LockWindowUpdate();
   for (int i=0;((i<pcdlg->m_pfiaInfos->GetSize()) && (!pcdlg->m_bAbort));i++) {
      lvi.iItem = i;
      lvi.lParam = (LPARAM) &(*pcdlg->m_pfiaInfos)[i];
      pcdlg->m_plstFiles->InsertItem(&lvi);
      pcdlg->m_ulProgress++;
   }
   pcdlg->m_plstFiles->UnlockWindowUpdate();

   pcdlg->m_bFillInProgress = FALSE;
   pcdlg->PostMessage(WM_SEARCH_FINISHED);
   return 0;
}

BOOL CompareContents(CFileInfo* pfinSrc, CFileInfo* pfinDest, const DWORD dwUpTo, 
   volatile BOOL* pbAbort) {
   // Note this method is not "win64 friendly"
   FILE* fSrc;
   FILE* fDest;
   DWORD dwCount = 0;
   BYTE cSrc, cDest;

   if ((dwUpTo == 0) || (dwUpTo > pfinSrc->GetLength())) 
      dwCount = pfinSrc->GetLength();
   else
      dwCount = dwUpTo;

   fSrc = fopen(pfinSrc->GetFilePath(),"rb");
   fDest = fopen(pfinDest->GetFilePath(),"rb");
   if (fSrc && fDest) {
      while (!feof(fSrc) && !feof(fDest) && !(*pbAbort) && (dwCount>0) && 
         fread(&cSrc,1,1,fSrc) && fread(&cDest,1,1,fDest) && (cSrc == cDest)) {
         dwCount--;
      }
   }
   if (fSrc) fclose(fSrc);
   if (fDest) fclose(fDest);
   return (dwCount == 0);
}

UINT CompareThread(CFCompareDlg *pcdlg) {
   CFileInfo* pfinSource;
   CFileInfo* pfinTarget;
   MATCHSTRUCT ms;
   DWORD dwMatchSize=0;
   DWORD dwMatchNumber=0;
   LVITEM lviMatched;
   // Compare
   
   int iSource=0;
   int iTarget=0;

   pcdlg->m_bCompareInProgress = TRUE;
   pcdlg->m_ulProgress = 0;
   pcdlg->m_ulProgressMax = pcdlg->m_fiaSourceInfos.GetSize() + 
      pcdlg->m_fiaTargetInfos.GetSize();

   while ((!pcdlg->m_bAbort) && 
          (iSource<pcdlg->m_fiaSourceInfos.GetSize()) && 
          (iTarget<pcdlg->m_fiaTargetInfos.GetSize())) {

      pfinSource = &pcdlg->m_fiaSourceInfos[iSource];
      pfinTarget = &pcdlg->m_fiaTargetInfos[iTarget];

      // Compare pfinSource with all the files of target with the same size of pfinSource
      for (int i=iTarget;(i<pcdlg->m_fiaTargetInfos.GetSize()) && 
         (pcdlg->m_fiaTargetInfos[i].GetLength64() == pfinSource->GetLength64());i++) {
         pfinTarget = &pcdlg->m_fiaTargetInfos[i];
         if (// Not the same file or Ignore duplicates unchecked
             (pcdlg->m_bCompareDuplicates ||
              (pfinSource->GetFilePath().CompareNoCase(pfinTarget->GetFilePath()) != 0)) &&
             // Checksum criteria
             ((pcdlg->m_nMatchCriteria != 1) || 
             (pfinSource->GetChecksum(pcdlg->m_dwUpto, FALSE, &pcdlg->m_bAbort) == 
              pfinTarget->GetChecksum(pcdlg->m_dwUpto, FALSE, &pcdlg->m_bAbort))) &&
             // CRC criteria
             ((pcdlg->m_nMatchCriteria != 2) || 
             (pfinSource->GetCRC(pcdlg->m_dwUpto, FALSE, &pcdlg->m_bAbort) == 
              pfinTarget->GetCRC(pcdlg->m_dwUpto, FALSE, &pcdlg->m_bAbort))) &&
             // Contents criteria
             ((pcdlg->m_nMatchCriteria != 3) || 
             (CompareContents(pfinSource, pfinTarget, pcdlg->m_dwUpto, &pcdlg->m_bAbort)))
             ) {
             // Match!
             ms.pfinTarget = pfinTarget;
             ms.pfinSource = pfinSource;
             pcdlg->m_msaMatchedInfos.InsertAt(pcdlg->m_msaMatchedInfos.GetSize(), ms);
             dwMatchSize += pfinTarget->GetLength();
             dwMatchNumber++;
         }
      }
      // Restore initial target
      pfinTarget = &pcdlg->m_fiaTargetInfos[iTarget];
      
      if (pfinTarget->GetLength64() >= pfinSource->GetLength64())
         iSource++; // In case of equal sizes, iSource must be incremented first 
                    // (see inner loop for reasons)
      else
         iTarget++;

      pcdlg->m_ulProgress++;
   }

   // Set new progress values
   pcdlg->m_ulProgress = 0;
   pcdlg->m_ulProgressMax = pcdlg->m_msaMatchedInfos.GetSize();

   pcdlg->m_bCompareInProgress = FALSE;
   pcdlg->m_bFillInProgress = TRUE;
   
   // Fill listview
   pcdlg->m_lblStatus.SetWindowText(_T("Filling listview..."));
   pcdlg->m_lstMatchedFiles.LockWindowUpdate();
   // Set common LVITEM values for all elements
   lviMatched.mask = LVIF_PARAM | LVIF_TEXT;
   lviMatched.iSubItem = 0;
   lviMatched.pszText = LPSTR_TEXTCALLBACK;
   for (int i=0;(i<pcdlg->m_msaMatchedInfos.GetSize()) && (!pcdlg->m_bAbort);i++) {
      lviMatched.iItem = i;
      lviMatched.lParam = (LPARAM) &pcdlg->m_msaMatchedInfos[i];
      pcdlg->m_lstMatchedFiles.InsertItem(&lviMatched);
      pcdlg->m_ulProgress++;
   }
   pcdlg->m_lstMatchedFiles.UnlockWindowUpdate();
   pcdlg->m_bFillInProgress = FALSE;

   // Report compare thread end
   pcdlg->PostMessage(WM_COMPARE_FINISHED,dwMatchNumber, dwMatchSize);
   return 0;
}

void CFCompareDlg::SetBusyState(int m_bsState) {
   this->m_bsState = m_bsState;

   GetDlgItem(LST_SOURCEFILES)->EnableWindow(m_bsState != BS_SEARCHING);
   GetDlgItem(LST_TARGETFILES)->EnableWindow(m_bsState != BS_SEARCHING);
   GetDlgItem(CHK_RECURSEDIR)->EnableWindow(m_bsState != BS_SEARCHING);

   GetDlgItem(BTN_COMPARE)->EnableWindow(m_bsState == BS_IDLE);
   GetDlgItem(BTN_CLEARSOURCEFILES)->EnableWindow(m_bsState == BS_IDLE);
   GetDlgItem(BTN_CLEARTARGETFILES)->EnableWindow(m_bsState == BS_IDLE);
   GetDlgItem(BTN_ADDSOURCEFILES)->EnableWindow(m_bsState == BS_IDLE);
   GetDlgItem(BTN_ADDTARGETFILES)->EnableWindow(m_bsState == BS_IDLE);
   GetDlgItem(BTN_EXPORT)->EnableWindow(m_bsState == BS_IDLE);

   GetDlgItem(RAD_MATCHCRITERIA)->EnableWindow(m_bsState != BS_COMPARING);
   GetDlgItem(RAD_MATCHCHECKSUM)->EnableWindow(m_bsState != BS_COMPARING);
   GetDlgItem(RAD_MATCHCRC)->EnableWindow(m_bsState != BS_COMPARING);
   GetDlgItem(RAD_MATCHCONTENTS)->EnableWindow(m_bsState != BS_COMPARING);
   GetDlgItem(EDT_UPTO)->EnableWindow((m_bsState != BS_COMPARING) && 
      (IsDlgButtonChecked(RAD_MATCHCRITERIA) != BST_CHECKED));
   GetDlgItem(CHK_COMPAREDUPLICATES)->EnableWindow(m_bsState != BS_COMPARING);

   if (m_bsState != BS_IDLE) {
      GetDlgItem(BTN_EXIT)->SetWindowText(_T("Cancel"));
   } else {
      GetDlgItem(BTN_EXIT)->SetWindowText(_T("Exit"));
      m_lblStatus.SetWindowText(_T("Ready"));
      m_prgProgress.SetPos(0);
   }
}


void CFCompareDlg::UpdateCounters() {
   CString str;

   str.Format(_T("Matched files (%d)"), m_lstMatchedFiles.GetItemCount());
   GetDlgItem(LBL_MATCHEDFILES)->SetWindowText(str);
   str.Format(_T("Source files (%d)"), m_lstSourceFiles.GetItemCount());
   GetDlgItem(LBL_SOURCEFILES)->SetWindowText(str);
   str.Format(_T("Target files (%d)"), m_lstTargetFiles.GetItemCount());
   GetDlgItem(LBL_TARGETFILES)->SetWindowText(str);
}

void CFCompareDlg::OnCompare() {
   // Retrieve comparison settings
   UpdateData(TRUE);
   m_lblStatus.SetWindowText(_T("Comparing files..."));

   // Empty compare list
   m_lstMatchedFiles.DeleteAllItems();
   m_msaMatchedInfos.RemoveAll();

   m_bAbort = FALSE;
   SetBusyState(BS_COMPARING);

   // Launch thread
   AfxBeginThread((AFX_THREADPROC) CompareThread,(LPVOID) this);
   // Launch progress-report timer
   if (SetTimer(1,250, NULL) == 0){
      // Timer not available
      AfxMessageBox(_T("Timer is not available, real time progress will not be reported."), 
         MB_ICONINFORMATION | MB_OK);
   }
}

void CFCompareDlg::OnGetdispinfoSearchFiles(NMHDR* pNMHDR, LRESULT* pResult) {
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
   CString str;
   CFileInfo* pinf;

   pinf = (CFileInfo*) pDispInfo->item.lParam;
   switch (pDispInfo->item.iSubItem) {
      case 0:
         pDispInfo->item.mask = LVIF_TEXT;
         StrCpyN(pDispInfo->item.pszText, pinf->GetFilePath(), pDispInfo->item.cchTextMax);
      break;
      case 1:
         if (pinf->GetLength64() < 1024) 
            str.Format("%d   B", pinf->GetLength());
         else {
            // The following weird LONGLONG casts are because VC 6.0 doesn't implement int64 
            // to double conversion =8-ooo
            if (pinf->GetLength64() < (1024*1024))
               str.Format("%3.2f KB",(LONGLONG) pinf->GetLength64() / 1024.0);
            else {
               if (pinf->GetLength64() < (1024*1024*1024))
                  str.Format("%3.2f MB", (LONGLONG) pinf->GetLength64() / (1024*1024.0));
               else
                  str.Format("%1.2f GB", (LONGLONG) pinf->GetLength64() / (1024.0*1024*1024));
            }
         }     
         pDispInfo->item.mask = LVIF_TEXT;

         StrCpyN(pDispInfo->item.pszText, str, pDispInfo->item.cchTextMax);
      break;
   }
	
	*pResult = 0;
}

void CFCompareDlg::OnTimer(UINT nIDEvent) {
   CString str;

   if (m_bAddInProgress) {
      str.Format(_T("Found %d files"),m_fiaSourceInfos.GetSize() + m_fiaTargetInfos.GetSize());
      m_lblStatus.SetWindowText(str);
      str.Format(_T("Source files (%d)"), m_fiaSourceInfos.GetSize());
      m_lblSourceFiles.SetWindowText(str);
      str.Format(_T("Target files (%d)"), m_fiaTargetInfos.GetSize());
      m_lblTargetFiles.SetWindowText(str);
   }

   if (m_bFillInProgress) {
      m_prgProgress.SetRange(0, (short) m_ulProgressMax);
      m_prgProgress.SetPos(m_ulProgress);
   }

   if (m_bCompareInProgress) {
      str.Format(_T("Matched %d files"), m_msaMatchedInfos.GetSize());
      m_lblStatus.SetWindowText(str);
      str.Format(_T("Matched files (%d)"), m_msaMatchedInfos.GetSize());
      m_lblMatchedFiles.SetWindowText(str);
      m_prgProgress.SetRange(0, (short) m_ulProgressMax);
      m_prgProgress.SetPos(m_ulProgress);
   }

	CDialog::OnTimer(nIDEvent);
}

afx_msg LONG CFCompareDlg::OnSearchFinished(WPARAM /* wparam*/ , LPARAM /* lparam */) {
   if (m_bAbort) {
      // Delete items from aborted listview
      m_plstFiles->DeleteAllItems();
      // Delete items from aborted CFileInfoArray
      m_pfiaInfos->RemoveAll();
   }

   KillTimer(1);
   SetBusyState(BS_IDLE);

   m_lblStatus.SetWindowText(_T("Ready"));
   UpdateCounters();
   if (m_bExit) PostQuitMessage(0);
   return TRUE;
}

afx_msg LONG CFCompareDlg::OnCompareFinished(WPARAM wparam, LPARAM lparam) {
   CString str;

   if (m_bAbort)
      m_lstMatchedFiles.DeleteAllItems();

   KillTimer(1);
   SetBusyState(BS_IDLE);

   if (!m_bAbort) {
      str.Format(_T("Comparison results:\r\n* %d files matched\r\n* %d bytes matched"),
         wparam, lparam);

      MessageBox(str,_T("Comparison ended"),MB_OK);
   }

   m_prgProgress.SetPos(0);

   str.Format(_T("Matched files (%d)"),m_lstMatchedFiles.GetItemCount());
   m_lblMatchedFiles.SetWindowText(str);
   
   UpdateCounters();
   if (m_bExit) PostQuitMessage(0);
   return TRUE;
}

void CFCompareDlg::OnExit() {
   if (m_bsState == BS_IDLE) PostQuitMessage(0);
   m_bAbort = TRUE;
}

void CFCompareDlg::OnSelchangeAction(NMHDR* /* pNMHDR*/, LRESULT* pResult) {
   if (m_tabAction.GetCurSel() == PPG_COMPARE) {
      GetDlgItem(LBL_TARGETFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(LBL_SOURCEFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(LST_TARGETFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(LST_SOURCEFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(BTN_COMPARE)->ShowWindow(SW_HIDE);
      GetDlgItem(LBL_MATCHEDFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(LST_MATCHEDFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(CHK_COMPAREDUPLICATES)->ShowWindow(SW_HIDE);
   } else {
      GetDlgItem(LBL_TARGETFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(LBL_SOURCEFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(LST_TARGETFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(LST_SOURCEFILES)->ShowWindow(SW_SHOW);
      GetDlgItem(BTN_COMPARE)->ShowWindow(SW_SHOW);
      GetDlgItem(LBL_MATCHEDFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(LST_MATCHEDFILES)->ShowWindow(SW_HIDE);
      GetDlgItem(CHK_COMPAREDUPLICATES)->ShowWindow(SW_SHOW);
   }
	*pResult = 0;
}

void CFCompareDlg::OnGetdispinfoMatchedFiles(NMHDR* pNMHDR, LRESULT* pResult)  {
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
   MATCHSTRUCT* pms;
   CFileInfo* pinf;
   CString str;

	switch (pDispInfo->item.iSubItem) {
      case 0:
         pDispInfo->item.mask = LVIF_TEXT;
         pms = (MATCHSTRUCT*) pDispInfo->item.lParam;
         StrCpyN(pDispInfo->item.pszText, pms->pfinSource->GetFilePath(), 
            pDispInfo->item.cchTextMax);
      break;
      case 1:
         pDispInfo->item.mask = LVIF_TEXT;
         pms = (MATCHSTRUCT*) pDispInfo->item.lParam;
         StrCpyN(pDispInfo->item.pszText, pms->pfinTarget->GetFilePath(), 
            pDispInfo->item.cchTextMax);
      break;
      case 2:
         pms = (MATCHSTRUCT*) pDispInfo->item.lParam;
         pinf = pms->pfinSource;

         if (pinf->GetLength64() < 1024) 
            str.Format("%d   B",pinf->GetLength());
         else {
            // The following weird LONGLONG casts are because VC 6.0 doesn't implement int64 
            // to double conversion =8-ooo
            if (pinf->GetLength64() < (1024*1024))
               str.Format("%3.2f KB",(LONGLONG) pinf->GetLength64() / 1024.0);
            else {
               if (pinf->GetLength64() < (1024*1024*1024))
                  str.Format("%3.2f MB", (LONGLONG) pinf->GetLength64() / (1024*1024.0));
               else
                  str.Format("%1.2f GB", (LONGLONG) pinf->GetLength64() / (1024.0*1024*1024));
            }
         }     
         pDispInfo->item.mask = LVIF_TEXT;

         StrCpyN(pDispInfo->item.pszText, str, pDispInfo->item.cchTextMax);
      break;
   }
	
	*pResult = 0;
}

void CFCompareDlg::OnAddSourceFiles() {
   // Retrieve search settings from dialog controls
   UpdateData(TRUE);

   // Append backslash if necessary
   if ((m_strDirectory.GetLength()>0) && 
       (m_strDirectory[m_strDirectory.GetLength()-1] != TCHAR('\\'))) m_strDirectory += TCHAR('\\');
   UpdateData(FALSE);

   // Set common variables with thread
   m_plstFiles = &m_lstSourceFiles;
   m_pfiaInfos = &m_fiaSourceInfos;

   // Compare text-callback list will no longer be up to date, empty it
   m_lstMatchedFiles.DeleteAllItems();
   m_msaMatchedInfos.RemoveAll();
   m_lblStatus.SetWindowText(_T("Inserting files..."));

   m_bAbort = FALSE;
   SetBusyState(BS_SEARCHING);

   // Launch thread
   AfxBeginThread((AFX_THREADPROC) SearchThread,(LPVOID) this);
   // Launch progress-report timer
   if (SetTimer(1,250, NULL) == 0){
      // Timer not available
      AfxMessageBox(_T("Timer is not available, real time progress will not be reported."), 
         MB_ICONINFORMATION | MB_OK);
   }
}

void CFCompareDlg::OnAddTargetFiles() {
   // Retrieve search settings from dialog controls
   UpdateData(TRUE);

   // Append backslash if necessary
   if ((m_strDirectory.GetLength()>0) && 
       (m_strDirectory[m_strDirectory.GetLength()-1] != TCHAR('\\'))) m_strDirectory += TCHAR('\\');
   UpdateData(FALSE);

   // Set common variables with thread
   m_plstFiles = &m_lstTargetFiles;
   m_pfiaInfos = &m_fiaTargetInfos;

   // Compare text-callback list will no longer be up to date, empty it
   m_lstMatchedFiles.DeleteAllItems();
   m_msaMatchedInfos.RemoveAll();
   m_lblStatus.SetWindowText(_T("Inserting files..."));

   m_bAbort = FALSE;
   SetBusyState(BS_SEARCHING);

   // Launch thread
   AfxBeginThread((AFX_THREADPROC) SearchThread,(LPVOID) this);
   // Launch progress-report timer
   if (SetTimer(1,250, NULL) == 0){
      // Timer not available
      AfxMessageBox(_T("Timer is not available, real time progress will not be reported."), 
         MB_ICONINFORMATION | MB_OK);
   }
}

void CFCompareDlg::OnClearSourceFiles() {
   // Clear matched
   m_lstMatchedFiles.DeleteAllItems();
   m_msaMatchedInfos.RemoveAll();
   // Clear listview
   m_lstSourceFiles.DeleteAllItems();
   // Clear fileinfos array
   m_fiaSourceInfos.RemoveAll();
   // UpdateCounters
   UpdateCounters();
}

void CFCompareDlg::OnClearTargetFiles() {
   // Clear matched
   m_lstMatchedFiles.DeleteAllItems();
   m_msaMatchedInfos.RemoveAll();
   // Clear listview
   m_lstTargetFiles.DeleteAllItems();
   // Clear fileinfos array
   m_fiaTargetInfos.RemoveAll();
   // Update counters
   UpdateCounters();
}

void CFCompareDlg::OnClose() {
   if (m_bsState != BS_IDLE) {
      // Thread running, signal abort and exit flag
      m_bExit = TRUE;
      m_bAbort = TRUE;
   } else
	   CDialog::OnClose();
}

void CFCompareDlg::OnExport() {
   CStdioFile sf;
   CFileDialog fd(FALSE);
   int i;
   CString str;

   if (fd.DoModal() == IDOK) {
      try {
         sf.Open(fd.m_ofn.lpstrFile, CStdioFile::modeWrite | CFile::modeCreate | CStdioFile::typeText);
         sf.WriteString(_T("SOURCE FILES:\n"));
         for (i=0;i<m_fiaSourceInfos.GetSize();i++)
            sf.WriteString("\""+m_fiaSourceInfos[i].GetFilePath() + "\"\n");
         sf.WriteString(_T("TARGET FILES:\n"));
         for (i=0;i<m_fiaTargetInfos.GetSize();i++)
            sf.WriteString("\"" + m_fiaTargetInfos[i].GetFilePath() + "\"\n");
         sf.WriteString(_T("MATCHED FILES:\n"));
         for (i=0;i<m_msaMatchedInfos.GetSize();i++) 
            sf.WriteString("\"" + m_msaMatchedInfos[i].pfinSource->GetFilePath() + "\"" + 
               " \\ " + "\"" + m_msaMatchedInfos[i].pfinTarget->GetFilePath() + "\"\n");
         sf.Close();
      } catch (CException e) {
         e.Delete();
      }

   }
}
